﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Xml.Linq;
using System.IO;
using System.Diagnostics;
using GenericLibrary.Xml;
using GenericLibrary.Xml.Xsl;
using GenericLibrary.Wcf;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.CodeDom.Compiler;

namespace GenericLibrary.Xml.Xsl {

    //[ServiceContract( Namespace = "GenericLibrary.Xml.Xsl", Name = "XmlDataTransformService" )]
    //public interface IXmlDataTransformService {

    //    [OperationContract]
    //    bool Prepare( Stream xslt );

    //    [OperationContract]
    //    Stream Transform( Stream input );
    //}

    public interface IXslTransformContext<T> where T : class, IXslTransformContext<T>, new() {

        XsltSettings GetSettings();
        XmlResolver GetResolver();
        XsltArgumentList GetArgumentList();
    }

    public class DefaultXslTransformContext : IXslTransformContext<DefaultXslTransformContext> {


        #region IXmlDataTransformServiceContext<DefaultXmlDataTransformServiceContext> Members

        public virtual XsltSettings GetSettings() {
            return new XsltSettings( false, true );
        }

        public virtual XmlResolver GetResolver() {
            return null;
        }

        public virtual XsltArgumentList GetArgumentList() {
            try {
                XsltArgumentList argumentList = new XsltArgumentList();
                XsltExtension extension = new XsltExtension();
                argumentList.AddExtensionObject( extension.NamespaceUri, extension );
                return argumentList;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        #endregion
    }

    class XslCompiledTransform<TContext> : MarshalByRefObject, IDisposable where TContext : class, IXslTransformContext<TContext>, new() {

        private XslCompiledTransform _xslt;
        private TContext _context;

        public AppDomain GetWorkingDomain() {
            return AppDomain.CurrentDomain;
        }

        public TempFileCollection GetTemporaryFiles() {
            try {
                return this._xslt.TemporaryFiles;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public bool Load( Stream xsltStream ) {
            try {
                using( xsltStream ) {
                    using( XmlReader reader = XmlReader.Create( xsltStream ) ) {
                        this._xslt.Load( reader, this._context.GetSettings(), this._context.GetResolver() );
                        return true;
                    }
                }
            } catch( Exception ) {
                Debug.Assert( false );
                return false;
            }
        }

        public Stream Transform( Stream input ) {
            try {
                using( input ) {
                    Stream output = new MemoryStream();
                    using( XmlReader reader = XmlReader.Create( input ) ) {
                        this._xslt.Transform( reader, this._context.GetArgumentList(), output );
                    }
                    output.Position = 0;
                    return output;
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public XslCompiledTransform() {
            try {
                this._xslt = new XslCompiledTransform( false );
                this._context = new TContext();
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        #region IDisposable Members

        public void Dispose() {
            try {
                if( this._context is IDisposable ) {
                    ( this._context as IDisposable ).Dispose();
                }
                this._xslt.TemporaryFiles.KeepFiles = false;
                this._xslt = null;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        #endregion
    }

    [ServiceBehavior( InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single )]
    public class XmlDataTransformService<TContext> : IXmlDataTransformService, IDisposable
        where TContext : class, IXslTransformContext<TContext>, new() {

        private XslCompiledTransform<TContext> _xslt;

        public XmlDataTransformService() { }

        #region IXmlDataTransformService Members

        public bool Prepare( Stream xsltStream ) {
            try {
                using( xsltStream ) {
                    if( this._xslt != null ) {
                        this.Dispose();
                    }
                    AppDomainSetup ads = new AppDomainSetup();
                    ads.ApplicationBase = System.Environment.CurrentDirectory;
                    ads.DisallowBindingRedirects = false;
                    ads.DisallowCodeDownload = true;
                    ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
                    AppDomain workingDomain = AppDomain.CreateDomain( "WorkingDomain", null, ads );
                    this._xslt = workingDomain.CreateInstanceAndUnwrap( typeof( XslCompiledTransform<TContext> ).Assembly.FullName, typeof( XslCompiledTransform<TContext> ).FullName ) as XslCompiledTransform<TContext>;
                    return this._xslt.Load( xsltStream );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public Stream Transform( Stream input ) {
            try {
                using( input ) {
                    return this._xslt.Transform( input );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose() {
            try {
                TempFileCollection temporaryFiles = this._xslt.GetTemporaryFiles();
                AppDomain.Unload( this._xslt.GetWorkingDomain() );
                this._xslt = null;
                temporaryFiles.KeepFiles = false;
                temporaryFiles.Delete();
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        #endregion

        /*
        svcutil.exe /language:cs /out:XmlDataTransformServiceClient.cs /config:app.config http://localhost:8000/Services
        */

        public static ServiceHost CreateHost( Uri uri ) {
            try {
                ServiceHost host = new ServiceHost( typeof( XmlDataTransformService<TContext> ), uri );

                ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                smb.HttpGetUrl = uri;
                smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
                host.Description.Behaviors.Add( smb );
                host.AddServiceEndpoint( ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "Mex" );

                WSHttpBinding basicBinding = new WSHttpBinding();
                basicBinding.ReceiveTimeout = new TimeSpan( 0, 5, 0 );
                basicBinding.SendTimeout = new TimeSpan( 0, 5, 0 );
                basicBinding.MaxReceivedMessageSize = int.MaxValue;//要进行数据压缩！

                host.AddServiceEndpoint( typeof( IXmlDataTransformService ), basicBinding, "XmlDataTransformService" );

                CustomBinding customBinding = GZipMessageEncodingBindingElement.PlugIn( basicBinding );
                customBinding.ReceiveTimeout = new TimeSpan( 0, 5, 0 );
                customBinding.SendTimeout = new TimeSpan( 0, 5, 0 );

                host.AddServiceEndpoint( typeof( IXmlDataTransformService ), customBinding, "XmlDataTransformServiceWithMessageZip" );

                return host;
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }
    }
}
